Skip to content

Refactor analytics and Statsig integration for improved client handling#2941

Merged
eldadfux merged 2 commits intomainfrom
feat/marketing-hero-statsig-layouts
Apr 29, 2026
Merged

Refactor analytics and Statsig integration for improved client handling#2941
eldadfux merged 2 commits intomainfrom
feat/marketing-hero-statsig-layouts

Conversation

@eldadfux
Copy link
Copy Markdown
Member

  • Updated the analytics module to lazily create the analytics client only in the browser, preventing unnecessary fetch calls during server-side rendering (SSR).
  • Enhanced the homepage hero component to support Statsig client initialization and query overrides, ensuring proper handling of hero layout and subtitle based on user experiments.
  • Introduced a new function to normalize hero subtitles for better consistency in experiment values.
  • Cleaned up Statsig server-related code by re-exporting necessary functions and types, streamlining the integration process.

This refactor aims to improve performance and maintainability while ensuring accurate experiment tracking and user experience.

What does this PR do?

(Provide a description of what this PR does.)

Test Plan

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)

Related PRs and Issues

(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)

Have you read the Contributing Guidelines on issues?

(Write your answer here.)

- Updated the analytics module to lazily create the analytics client only in the browser, preventing unnecessary fetch calls during server-side rendering (SSR).
- Enhanced the homepage hero component to support Statsig client initialization and query overrides, ensuring proper handling of hero layout and subtitle based on user experiments.
- Introduced a new function to normalize hero subtitles for better consistency in experiment values.
- Cleaned up Statsig server-related code by re-exporting necessary functions and types, streamlining the integration process.

This refactor aims to improve performance and maintainability while ensuring accurate experiment tracking and user experience.
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 29, 2026

Greptile Summary

This PR refactors the Statsig and analytics integration into a cleaner client/server split: analytics is now lazily initialized browser-side only (fixing SSR fetch calls), and the Statsig server code is modularized into server.ts + experiments/marketing-hero-{client,server}.ts with a thin re-export barrel kept for backward compatibility.

  • The initStatsig call was moved into hero.svelte / custom-hero.svelte's onMount, but client.ts's JSDoc recommends calling it from afterNavigate — on repeated SPA visits to / the fresh bootstrap from the server load is silently skipped because appliedBootstrapPayload is already set.

Confidence Score: 4/5

Safe to merge; no data loss or security issues found, all findings are style/design concerns.

All findings are P2: the sentinel collision is theoretically possible but practically negligible, the truncation is intentional, and the onMount vs afterNavigate concern only affects repeated SPA navigations back to /. No P0/P1 issues found.

src/routes/(marketing)/(components)/hero.svelte and src/lib/components/homepage-variations/custom-hero.svelte for the initStatsig call placement; src/lib/statsig/experiment-eval.ts for the sentinel approach.

Important Files Changed

Filename Overview
src/lib/actions/analytics.ts Lazy client creation guarded by browser prevents Analytics() / plausible() from running during SSR; trackEvent now short-circuits early for non-browser calls.
src/lib/statsig/server.ts New file extracting the Node Statsig singleton from the old monolithic hero-statsig.server.ts; logic is identical to what was there before.
src/lib/statsig/experiment-eval.ts New shared module for reading layout variants from Statsig evaluations; uses a numeric sentinel (-999_999) to distinguish 'param absent' from 'param present with value'.
src/lib/statsig/experiments/marketing-hero-server.ts Moves hero SSR evaluation out of the old barrel; adds multi-key layout lookup and dynamic-config fallback; description result now goes through normalizeHeroSubtitle (new truncation at 560 chars).
src/routes/(marketing)/(components)/hero.svelte Now calls initStatsig inside onMount (previously relied on an external caller); adds clientHeroLayout / clientHeroSubtitle reactive state filled after Statsig init.
src/lib/components/homepage-variations/custom-hero.svelte Mirrors hero.svelte Statsig init pattern; adds onMount with initStatsig + whenStatsigReady and reactive client state for layout/subtitle overrides.
src/routes/(marketing)/+page.server.ts Adds a building early-return that yields static defaults without per-user Statsig data; removes the now-redundant ternary on url.searchParams.

Comments Outside Diff (2)

  1. src/lib/statsig/experiment-eval.ts, line 267 (link)

    P2 Sentinel value collision risk

    LAYOUT_GET_SENTINEL = -999_999 is used to distinguish "param absent" from "param present with this value". If a Statsig experiment group is ever configured with layout = -999999 (e.g. a misconfigured test group), evaluation.get(key, LAYOUT_GET_SENTINEL) returns -999999 and the code treats it as "not found", silently falling through to .value or the fallback instead of applying that value. A string sentinel avoids the ambiguity entirely since the param type is numeric.

  2. src/lib/statsig/experiments/marketing-hero-server.ts, line 456-457 (link)

    P2 Undocumented truncation on server-evaluated description

    normalizeHeroSubtitle (via evaluateHeroDescriptionExperiment) now silently truncates experiment descriptions to 560 characters on the server side. The old code returned the raw Statsig string unmodified. Any experiment variant with a description longer than 560 chars will be cut off in SSR HTML — worth an explicit comment here so experiment authors are aware.

Reviews (1): Last reviewed commit: "format" | Re-trigger Greptile

Comment on lines 74 to 79
if (hasHeroExperimentQueryOverrides(page.url.searchParams)) {
log('URL query overrides (client layout)', {
...resolveHeroQueryOverrides(page.url.searchParams, {
heroLayout,
heroSubtitle: subtitle,
heroLayout: clientHeroLayout ?? heroLayout,
heroSubtitle: clientHeroSubtitle ?? subtitle,
heroTitle: title
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 initStatsig called from onMount instead of afterNavigate

The initStatsig JSDoc in client.ts explicitly says: "Call from afterNavigate so client-side navigations (e.g. /docs → /) still receive bootstrap." On an SPA navigation back to /, onMount fires again but appliedBootstrapPayload is already set, so the re-init branch is skipped and the fresh bootstrap from the new server load is never applied. The same pattern appears in custom-hero.svelte.

if (!browser) return null;
if (!analyticsClient) {
analyticsClient = Analytics({
app: 'appwrite',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Unhandled rejection on .then() with no arguments

getAnalyticsClient()?.track(name, data).then() attaches .then() solely to suppress the floating-promise lint warning, but any rejection from track() becomes an unhandled promise rejection. Prefer void casting instead:

Suggested change
app: 'appwrite',
void getAnalyticsClient()?.track(name, data);

@eldadfux eldadfux merged commit 74a8262 into main Apr 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant